#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Subsystems/SubsystemPickables.h"

#include <cfloat>

#include "ServerConfigure.h"
#include "Log.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"


#include "Terrain/TerrainSerializer23.h"
#include "Terrain/TerrainContentsGeneratorFlat.h"
#include "Terrain/TerrainContentsGenerator23.h"
#include "Terrain/TerrainUpdater.h"

#include "Managers/GameManager.h"
#include "Managers/BlocksManager.h"
#include "Managers/ChangeManager.h"

#include "Game/BlockDropValue.hpp"
#include "Network/Packages/SubsystemTerrainPackage.h"



namespace PocketSurvival
{

    SubsystemTerrain::SubsystemTerrain() {}

    SubsystemTerrain::~SubsystemTerrain()
    {
        for (int32_t index = 0; index < m_chunkCatchVector.size(); index++)
        {
            delete[] m_chunkCatchVector[index];
        }
    }

    TerrainChunk *SubsystemTerrain::getTerrainChunk(int32_t coordX, int32_t coordY)
    {
        std::lock_guard<SpinLock> lk(m_chunkMapLock);
        auto it = m_chunkMap.find(Point2(coordX, coordY));
        if (it != m_chunkMap.end() && it->second->isRemove == false)
        {
            return it->second;
        }
        return nullptr;
    }

    void SubsystemTerrain::loadTerrrainChunk(int32_t coordX, int32_t coordY)
    {
        m_waitLoadChunkQueue.enqueue(Point2(coordX, coordY));
    }

    int32_t SubsystemTerrain::getCellValue(int32_t x, int32_t y, int32_t z)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr && y > 0 && y < 256)
        {
            return chunk->getCellValueFast(x & 0x0f, y, z & 0x0f);
        }
        return 0;
    }

    void SubsystemTerrain::setCellValue(int32_t x, int32_t y, int32_t z, int32_t value)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr && y > 0 && y < 256)
        {
            return chunk->setCellValueFast(x & 0x0f, y, z & 0x0f, value);
        }
    }

    int32_t SubsystemTerrain::calculateTopmostCellHeight(int32_t x, int32_t z)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr)
        {
            return chunk->calculateTopmostCellHeight(x & 0xF, z & 0xF);
        }
        return 0;
    }

    int32_t SubsystemTerrain::getShaftValue(int32_t x, int32_t z)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr)
        {
            return chunk->getShaftValueFast(x & 0xF, z & 0xF);
        }
        return 0;
    }

    void SubsystemTerrain::setShaftValue(int32_t x, int32_t z, int32_t value)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr)
        {
            chunk->setShaftValueFast(x & 0xF, z & 0xF, value);
        }
    }

    std::shared_ptr<TerrainRaycastResult> SubsystemTerrain::raycast(const Vector3 &start, const Vector3 &theEnd, bool useInteractionBoxes, bool skipAirBlocks, std::function<bool(int32_t, float)> func)
    {
        Vector3 end = theEnd;
        float num = Vector3::Distance(start, end);
        if (num > 1000.0f)
        {
            Log::Warn("地形光线投射过长，正在修剪.", Log::LogType::TERRAIN);
            end = start + 1000.0f * Vector3::Normalize(end - start);
        }
        Ray3 ray(start, Vector3::Normalize(end - start));
        float x = start.posX;
        float y = start.posY;
        float z = start.posZ;
        float x2 = end.posX;
        float y2 = end.posY;
        float z2 = end.posZ;
        int32_t num2 = Terrain::ToCell(x);
        int32_t num3 = Terrain::ToCell(y);
        int32_t num4 = Terrain::ToCell(z);
        int32_t num5 = Terrain::ToCell(x2);
        int32_t num6 = Terrain::ToCell(y2);
        int32_t num7 = Terrain::ToCell(z2);
        int32_t num8 = (x < x2) ? 1 : ((x > x2) ? (-1) : 0);
        int32_t num9 = (y < y2) ? 1 : ((y > y2) ? (-1) : 0);
        int32_t num10 = (z < z2) ? 1 : ((z > z2) ? (-1) : 0);
        float num11 = MathUtils::Floor<float>(x);
        float num12 = num11 + 1.0f;
        float num13 = ((x > x2) ? (x - num11) : (num12 - x)) / std::abs(x2 - x);
        float num14 = MathUtils::Floor<float>(y);
        float num15 = num14 + 1.0f;
        float num16 = ((y > y2) ? (y - num14) : (num15 - y)) / std::abs(y2 - y);
        float num17 = MathUtils::Floor<float>(z);
        float num18 = num17 + 1.0f;
        float num19 = ((z > z2) ? (z - num17) : (num18 - z)) / std::abs(z2 - z);
        float num20 = 1.0f / std::abs(x2 - x);
        float num21 = 1.0f / std::abs(y2 - y);
        float num22 = 1.0f / std::abs(z2 - z);
        for (;;)
        {
            BoundingBox boundingBox;
            int32_t collisionBoxIndex = 0;
            std::shared_ptr<float> num23 = nullptr;
            int32_t cellValue = getCellValue(num2, num3, num4);
            int32_t num24 = Terrain::ExtractContents(cellValue);
            if (num24 != 0 || !skipAirBlocks)
            {
                Ray3 ray2(ray.Position - Vector3(num2, num3, num4), ray.Direction);
                int32_t nearestBoxIndex;
                BoundingBox nearestBox;
                std::shared_ptr<float> num25 = GameSingleton::blocksManager->blocks[num24]->Raycast(ray2, cellValue, useInteractionBoxes, nearestBoxIndex, nearestBox);
                if (num25 != nullptr && (num23 == nullptr || *num25 < *num23))
                {
                    num23 = num25;
                    collisionBoxIndex = nearestBoxIndex;
                    boundingBox = nearestBox;
                }
            }
            if (num23 != nullptr && *num23 <= num && (func == nullptr || func(cellValue, *num23)))
            {
                int32_t face = 0;
                Vector3 vector = start - Vector3(num2, num3, num4) + *num23 * ray.Direction;
                float num26 = FLT_MAX;
                float num27 = std::abs(vector.posX - boundingBox.Min.posX);
                if (num27 < num26)
                {
                    num26 = num27;
                    face = 3;
                }
                num27 = std::abs(vector.posX - boundingBox.Max.posX);
                if (num27 < num26)
                {
                    num26 = num27;
                    face = 1;
                }
                num27 = std::abs(vector.posY - boundingBox.Min.posY);
                if (num27 < num26)
                {
                    num26 = num27;
                    face = 5;
                }
                num27 = std::abs(vector.posY - boundingBox.Max.posY);
                if (num27 < num26)
                {
                    num26 = num27;
                    face = 4;
                }
                num27 = std::abs(vector.posZ - boundingBox.Min.posZ);
                if (num27 < num26)
                {
                    num26 = num27;
                    face = 2;
                }
                num27 = std::abs(vector.posZ - boundingBox.Max.posZ);
                if (num27 < num26)
                {
                    face = 0;
                }
                std::shared_ptr<TerrainRaycastResult> value = std::make_shared<TerrainRaycastResult>();
                value->ray = ray;
                value->value = cellValue;

                value->cellFace.point.posX = num2;
                value->cellFace.point.posY = num3;
                value->cellFace.point.posZ = num4;
                value->cellFace.face = face;

                value->collisionBoxIndex = collisionBoxIndex;
                value->distance = *num23;
                return value;
            }
            if (num13 <= num16 && num13 <= num19)
            {
                if (num2 == num5)
                {
                    break;
                }
                num13 += num20;
                num2 += num8;
            }
            else if (num16 <= num13 && num16 <= num19)
            {
                if (num3 == num6)
                {
                    break;
                }
                num16 += num21;
                num3 += num9;
            }
            else
            {
                if (num4 == num7)
                {
                    break;
                }
                num19 += num22;
                num4 += num10;
            }
        }
        return nullptr;
    }

    void SubsystemTerrain::changeCell(int32_t x, int32_t y, int32_t z, int32_t value, ComponentMiner *miner)
    {
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if (chunk != nullptr && y > 0 && y < 256)
        {
            int32_t cellValue = chunk->getCellValueFast(x & 0x0f, y, z & 0x0f);
            value = Terrain::ReplaceLight(value, 0);
            cellValue = Terrain::ReplaceLight(cellValue, 0);
            if (value == cellValue)
            {
                return;
            }
            GameSingleton::changeManager->terrainChange(Point3(x, y, z));

            chunk->setCellValueFast(x & 0x0f, y, z & 0x0f, value);

            chunk->State = TerrainChunkState::InvalidLight;
            chunk->recycleTime = 0.0f;
            chunk->isChange = true;

            // 方块改变的话，周围的方块需要被触发
            // 注意，这里的 changeCell 有可能是 processModifiedCells 函数中处理周围方块是调用到的方块行为进行的调用
            // 此时 m_modifiedSet 在遍历，不能添加或删除元素，所以需要一个额外的存储空间来存放改变的方块位置，这里用到 m_modifiedSet
            // 还有就是由于多线程处理的原因，给 m_modifiedSet 添加元素时，其它线程会调用到 processModifiedCells 函数，所以需要加锁来保证数据不冲突
            m_modifiedLock.lock();
            m_modifiedSet.insert(Point3(x, y, z));
            m_modifiedLock.unlock();

            int32_t num = Terrain::ExtractContents(cellValue);
            int32_t num2 = Terrain::ExtractContents(value);

            if (num2 != num)
            {
                std::vector<SubsystemBlockBehavior *> &blockBehaviors = m_blockBehaviors->getBlockBehaviors(num);
                for (auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
                {
                    (*behaviorIt)->onBlockRemoved(cellValue, value, x, y, z);
                }
                std::vector<SubsystemBlockBehavior *> &blockBehaviors2 = m_blockBehaviors->getBlockBehaviors(num2);
                for (auto behaviorIt2 = blockBehaviors2.begin(); behaviorIt2 != blockBehaviors2.end(); ++behaviorIt2)
                {
                    (*behaviorIt2)->onBlockAdded(value, cellValue, x, y, z);
                    if (miner != nullptr)
                    {
                        (*behaviorIt2)->onBlockAdded(value, cellValue, x, y, z, miner);
                    }
                }
            }
            else
            {
                std::vector<SubsystemBlockBehavior *> &blockBehaviors3 = m_blockBehaviors->getBlockBehaviors(num2);
                for (auto behaviorIt3 = blockBehaviors3.begin(); behaviorIt3 != blockBehaviors3.end(); ++behaviorIt3)
                {
                    (*behaviorIt3)->onBlockModified(value, cellValue, x, y, z);
                }
            }
        }
    }

    bool SubsystemTerrain::changeOldCell(int32_t x, int32_t y, int32_t z, int32_t oldValue, int32_t newValue, ComponentMiner *miner)
    {
        if (oldValue == newValue)
        {
            return false;
        }
        TerrainChunk *chunk = getTerrainChunk(x >> 4, z >> 4);
        if(chunk != nullptr  && y > 0 && y < 256)
        {
            if(chunk->changeOldCellValue(x & 0x0F, y, z & 0x0F, oldValue, newValue))
            {

                std::shared_ptr<IPackage> package = std::make_shared<SubsystemTerrainPackage>(x, y, z, newValue);
                GameSingleton::packageManager->addSendPackageToQueue(package);

                chunk->State = TerrainChunkState::InvalidLight;
                chunk->recycleTime = 0.0f;
                chunk->isChange = true;

                // 方块改变的话，周围的方块需要被触发
                // 注意，这里的 changeCell 有可能是 processModifiedCells 函数中处理周围方块是调用到的方块行为进行的调用
                // 此时 m_modifiedSet 在遍历，不能添加或删除元素，所以需要一个额外的存储空间来存放改变的方块位置，这里用到 m_modifiedSet
                // 还有就是由于多线程处理的原因，给 m_modifiedSet 添加元素时，其它线程会调用到 processModifiedCells 函数，所以需要加锁来保证数据不冲突
                m_modifiedLock.lock();
                m_modifiedSet.insert(Point3(x, y, z));
                m_modifiedLock.unlock();

        	    int32_t num = Terrain::ExtractContents(oldValue);
        	    int32_t num2 = Terrain::ExtractContents(newValue);

        	    if (num2 != num)
        	    {
                    std::vector<SubsystemBlockBehavior *> &blockBehaviors = m_blockBehaviors->getBlockBehaviors(num);
                    for(auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
                    {
                        (*behaviorIt)->onBlockRemoved(oldValue, newValue, x, y, z);
                    }
                    std::vector<SubsystemBlockBehavior *> &blockBehaviors2 = m_blockBehaviors->getBlockBehaviors(num2);
                    for(auto behaviorIt2 = blockBehaviors2.begin(); behaviorIt2 != blockBehaviors2.end(); ++behaviorIt2)
                    {
                        (*behaviorIt2)->onBlockAdded(newValue, oldValue, x, y, z);
                        if(miner != nullptr)
                        {
                            (*behaviorIt2)->onBlockAdded(newValue, oldValue, x, y, z, miner);
                        }
                    }
                }
        	    else
        	    {
                    std::vector<SubsystemBlockBehavior *> &blockBehaviors3 = m_blockBehaviors->getBlockBehaviors(num2);
                    for(auto behaviorIt3 = blockBehaviors3.begin(); behaviorIt3 != blockBehaviors3.end(); ++behaviorIt3)
                    {
                        (*behaviorIt3)->onBlockModified(newValue, oldValue, x, y, z);
                    }
        	    }
                return true;
            }
        }
        return false;
    }

    void SubsystemTerrain::destroyCell(int32_t toolLevel, int32_t x, int32_t y, int32_t z, int32_t newValue, bool noDrop, ComponentMiner *miner)
    {
        int32_t cellValue = getCellValue(x, y, z);
        int32_t num = Terrain::ExtractContents(cellValue);
        if (num != 0)
        {
            Block *block = GameSingleton::blocksManager->blocks[num];
            if (noDrop == false)
            {
                std::vector<BlockDropValue> dropValues;
                block->getDropValues(cellValue, newValue, toolLevel, dropValues);
                for (auto it = dropValues.begin(); it != dropValues.end(); it++)
                {
                    if (it->count > 0)
                    {
                        std::vector<SubsystemBlockBehavior *> &blockBehaviors = m_blockBehaviors->getBlockBehaviors(Terrain::ExtractContents(it->value));

                        int32_t onItemHarvestedNewValue = 0;
                        for (auto &behavior : blockBehaviors)
                        {
                            behavior->onItemHarvested(x, y, z, cellValue, *it, onItemHarvestedNewValue);
                        }
                        if (newValue == 0)
                        {
                            newValue = onItemHarvestedNewValue;
                        }
                        if (it->count > 0 && Terrain::ExtractContents(it->value) != 0)
                        {
                            Vector3 position = Vector3(x + 0.5f, y + 0.5f, z + 0.5f);

                            m_subsystemPickables->addPickable(it->value, it->count, position, nullptr, nullptr);
                        }
                    }
                }
            }
        }
        changeCell(x, y, z, newValue, miner);
    }

    void SubsystemTerrain::processModifiedCells()
    {
        static std::vector<Point3> neighborOffsets{
            Point3(0, 0, 0),
            Point3(-1, 0, 0),
            Point3(1, 0, 0),
            Point3(0, -1, 0),
            Point3(0, 1, 0),
            Point3(0, 0, -1),
            Point3(0, 0, 1)};
        m_modifiedLock.lock();
        for (auto it = m_modifiedSet.begin(); it != m_modifiedSet.end(); ++it)
        {
            m_modifiedVec.push_back(*it);
        }
        m_modifiedSet.clear();
        m_modifiedLock.unlock();

        for (auto it = m_modifiedVec.begin(); it != m_modifiedVec.end(); ++it)
        {
            for (auto it2 = neighborOffsets.begin(); it2 != neighborOffsets.end(); ++it2)
            {
                Point3 point = (*it) + (*it2);
                int32_t cellContents = getCellContents(point.posX, point.posY, point.posZ);
                std::vector<SubsystemBlockBehavior *> &blockBehaviors = m_blockBehaviors->getBlockBehaviors(cellContents);
                for (auto behaviorIt = blockBehaviors.begin(); behaviorIt != blockBehaviors.end(); ++behaviorIt)
                {
                    (*behaviorIt)->onNeighborBlockChanged(point.posX, point.posY, point.posZ, it->posX, it->posY, it->posZ);
                }
            }
        }
        m_modifiedVec.clear();
    }

    bool SubsystemTerrain::createChunkCatch(int32_t count)
    {
        TerrainChunk *terrainChunks = new TerrainChunk[count];
        m_chunkCatchVector.push_back(terrainChunks);
        Log::Info(fmt::format("创建了 {} 个区块的缓存", count), Log::LogType::TERRAIN);
        for (int32_t index = 0; index < count; index++)
        {
            m_chunkQueue.enqueue(&(terrainChunks[index]));
        }
        return true;
    }

    TerrainChunk *SubsystemTerrain::getAvailableChunk(int32_t coordX, int32_t coordY)
    {
        TerrainChunk *terrainChunk = nullptr;
        if (m_chunkQueue.try_dequeue(terrainChunk))
        {
            // Log::info("取出了一个区块", Log::LogType::TERRAIN);
            terrainChunk->initialize(coordX, coordY);
            return terrainChunk;
        }
        if (createChunkCatch(GameSingleton::serverConfigure->chunkCatchCount))
        {
            if (m_chunkQueue.try_dequeue(terrainChunk))
            {
                // Log::info("创建后取出了一个区块", Log::LogType::TERRAIN);
                terrainChunk->initialize(coordX, coordY);
                return terrainChunk;
            }
            Log::Error("增加 区块 缓存后无法获取到新的 区块");
        }
        return nullptr;
    }

    void SubsystemTerrain::saveAllChunk()
    {
        m_chunkMapLock.lock();
        for (auto it = m_chunkMap.begin(); it != m_chunkMap.end(); ++it)
        {
            TerrainChunk *terrainChunk = it->second;
            // 有发成改变的才需要保存处理
            if (terrainChunk->isChange == true)
            {
                // 直接扔进线程池中进行
                GameSingleton::gameThreadPool->push_task([terrainChunk, this]
                {
                    terrainSerializer->saveChunk(terrainChunk);
                    terrainChunk->isChange = false;
                });
            }
        }
        m_chunkMapLock.unlock();

        GameSingleton::gameThreadPool->wait_for_tasks();
    }

    void SubsystemTerrain::getAllTerrainChunkCoords(std::vector<Point2> &outCoordsVec)
    {
        m_chunkMapLock.lock();
        for (auto it = m_chunkMap.begin(); it != m_chunkMap.end(); ++it)
        {
            if (it->second->isRemove == false)
            {
                outCoordsVec.push_back(it->first);
            }
        }
        m_chunkMapLock.unlock();
    }

    void SubsystemTerrain::getAllTerrainChunk(std::vector<TerrainChunk *> &outChunkVec)
    {
        m_chunkMapLock.lock();
        for (auto it = m_chunkMap.begin(); it != m_chunkMap.end(); ++it)
        {
            if (it->second->isRemove == false)
            {
                outChunkVec.push_back(it->second);
            }
        }
        m_chunkMapLock.unlock();
    }


    void SubsystemTerrain::update(float dt)
    {        
        // ========================== 回收区块 ===========================
        std::vector<Point2> removeChunkVec;
        m_chunkMapLock.lock();
        for(auto it = m_chunkMap.cbegin(); it != m_chunkMap.cend(); ++it)
        {
            if(it->second->isRemove == true)
            {
                removeChunkVec.push_back(it->first);
            }
        }
        m_chunkMapLock.unlock();

        for (const Point2& removeChunkPoint : removeChunkVec)
        {
            TerrainChunk *terrainChunk = nullptr;
            m_chunkMapLock.lock();
            auto findIt = m_chunkMap.find(removeChunkPoint);
            if (findIt != m_chunkMap.end())
            {
                terrainChunk = findIt->second;
                m_chunkMap.erase(findIt);
            }
            m_chunkMapLock.unlock();

            if (terrainChunk != nullptr)
            {
                // 通知所有子系统，有区块将要被回收处理
                for (auto &pair : GameSingleton::gameManager->subsystemMap)
                {
                    pair.second->onTerrainChunkRemoved(terrainChunk);
                }

                // 区块有改变的话，则需要保存处理
                if (terrainChunk->isChange == true)
                {
                    // 这里用到保存线程池
                    GameSingleton::gameSaveThreadPool->push_task([terrainChunk,this]
                    {
                        terrainSerializer->saveChunk(terrainChunk);
                        m_chunkQueue.enqueue(terrainChunk);
                    });
                }
                else
                {
                    m_chunkQueue.enqueue(terrainChunk);
                }
            }
            else
            {
                Log::Error("地形回收错误!!");
            }
        }
        // =================================================================


        TerrainChunk *loadTerrainChunk;
        while(m_loadFinishChunkQueue.try_dequeue(loadTerrainChunk))
        {
            for (auto &pair : GameSingleton::gameManager->subsystemMap)
            {
                pair.second->onTerrainChunkAdded(loadTerrainChunk);
            }
        }
        

        // 只有将需要回收的区块回收处理完后才能调用地形刷新和后面的地形生成
        terrainUpdater->update(dt);

        GameSingleton::gameThreadPool->push_task(&SubsystemTerrain::processModifiedCells, this);

        Point2 point;
        while(m_waitLoadChunkQueue.try_dequeue(point))
        {
            GameSingleton::gameNoWaitThreadPool->push_task(&SubsystemTerrain::loadChunk, this, point);
        }
    }

    void SubsystemTerrain::loadChunk(Point2 point)
    {
        TerrainChunk *terrainChunk = nullptr;

        m_chunkMapLock.lock();
        if(m_chunkMap.find(point) != m_chunkMap.end())
        {
            m_chunkMapLock.unlock();
            return;
        }
        terrainChunk = getAvailableChunk(point.posX, point.posY);
        m_chunkMap[point] = terrainChunk;
        m_chunkMapLock.unlock();

        terrainUpdater->loadChunk(terrainChunk);
        m_loadFinishChunkQueue.enqueue(terrainChunk);
    }


    void SubsystemTerrain::load(const nlohmann::json &json)
    {
        TerrainGenerationMode terrainMode = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>()->terrainGenerationMode;

        m_blockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>();
        m_subsystemPickables = GameSingleton::singletonPtr->getSingleton<SubsystemPickables>();

        if(terrainMode == TerrainGenerationMode::FlatIsland || terrainMode == TerrainGenerationMode::FlatContinent)
        {
            terrainContentsGenerator = std::make_shared<TerrainContentsGeneratorFlat>();
        }
        else
        {
            terrainContentsGenerator = std::make_shared<TerrainContentsGenerator23>();
        }
        
        terrainUpdater = std::make_shared<TerrainUpdater>();
        terrainSerializer = std::make_shared<TerrainSerializer23>();

        m_saveRegionTime = 0;

        createChunkCatch(GameSingleton::serverConfigure->chunkCatchCount);

        Utils::GetJsonValue<int32_t>(json, m_seasonTemperature, "SeasonTemperature", 0);
        Utils::GetJsonValue<int32_t>(json, m_seasonHumidity, "SeasonHumidity", 0);
    }

    void SubsystemTerrain::save(nlohmann::json &json)
    {
        json["SeasonTemperature"] = m_seasonTemperature;
        json["SeasonHumidity"] = m_seasonHumidity;

        m_saveRegionTime = 0.0f;
        saveAllChunk();
        // GameSingleton::gameThreadPool->wait_for_tasks();
        terrainSerializer->saveAndReleaseAllRegion();
    }

    void SubsystemTerrain::saveToMsgJson(nlohmann::json &jsonData) {}



    static const std::string subsystemName = "Terrain";
    const std::string &SubsystemTerrain::getName() const
    {
        return subsystemName;
    }
    SubUpdateType SubsystemTerrain::getUpdateType()
    {
        return SubUpdateType::MultiThreadUpdate;
    }
    SubLoadEchelon SubsystemTerrain::getLoadEchelon()
    {
        return SubLoadEchelon::Second;
    }
}